/**
 * 内存初始化的一些主要操作。
 */
#ifndef _AHAMOLETT_MEMORY_H_
#define _AHAMOLETT_MEMORY_H_

#include <mm/mm_types.h>

/* 最大的NUMA节点数量。 */
#define MAX_PGLISTS_NUM 1

/* 储存单个NUMA节点的内存信息。因为目前没有实现NUMA，所以其长度恒为1。 */
extern struct pglist_struct pglist[MAX_PGLISTS_NUM];

/**
 * @brief 初始化全局内存信息结构体。
 * 
 * 初始化所以的pglist结构体，并事先分配 zones 与各个 pglist 结构体需要的内存空间。初始化
 * 结束后所有关于内存区域与内存页的信息都为空。
 * 
 * 需要依赖 early_alloc.h，pages.h 和 physical_area.h，所以进行初始化以前需要先完成两者
 * 的初始化。
 * 
 * @return int 初始化成功返回0，失败返回-1。
 */
extern int pglist_init(unsigned long nodes);

/**
 * @brief 获得系统中 pglist 结构体的总个数。
 * 
 * @return unsigned long 系统中 pglist 结构体的总个数。
 */
extern unsigned long get_pglists_count(void);

/**
 * @brief 获得给定NUMA编号的 pglist 结构体。
 * 
 * @param node_num pglist的NUMA编号。
 * 
 * @return struct pglist_struct* 成功返回对应的 pglist 结构体指针，失败返回NULL。
 */
extern struct pglist_struct *get_node_pglist(unsigned long node_num);

/**
 * @brief 得到当前NUMA节点的 pglist 结构体。
 * 
 * 对于有多个NUMA节点的情况，通过该函数可以获得当前NUMA节点所对应的 pglist 结构体。如果系统
 * 没有开启NUMA，则总是返回同一个 pglist 结构体。
 * 
 * 当前没有实现NUMA，所以总是返回同一个 pglist 结构体。
 * 
 * @return struct pglist_struct* 成功返回当前NUMA节点的 pglist 结构体地址，
 *                               失败返回NULL。
 */
extern struct pglist_struct *get_current_pglist(void);

/**
 * @brief 遍历系统中的所有 pglist 结构体。
 * 
 * @param pos unsigned long 作为游标遍历所有 pglist 结构体。
 * @param pglist struct pglist_struct* 存放当前遍历到的 pglist 结构体。
 */
#define for_each_pglist(pos, pglist)    \
    for (   (pos) = 0, (pglist) = get_node_pglist(0);   \
            (pglist) != NULL;   \
            (pos)++, (pglist) = get_node_pglist(pos)    \
    )

/**
 * @brief 初始化内存区域结构体。
 * 
 * 初始化各个内存区域结构体，完善各个 page 和 zone 结构体指向其父结构体的引用，并初始化
 * 伙伴系统。初始化结束后伙伴系统的空闲列表为空。
 * 
 * 需要在 pglist_init 后进行初始化。
 * 
 * @return int 初始化成功返回0，失败返回-1。
 */
extern int mem_zones_init(void);

/**
 * @brief 获得系统中所有内存区域结构体的数量。
 * 
 * @return unsigned long 系统中所有内存区域结构体的数量。
 */
extern unsigned long get_global_zones_count(void);

/**
 * @brief 获得指定 pglist 中存放内存区域结构体的数组中储存的结构体数量。
 * 
 * @param pglist pglist 结构体。
 * 
 * @return unsigned long 指定 pglist 中存放内存区域结构体的数组中储存的结构体数量。
 */
extern unsigned long get_pglist_zones_count(struct pglist_struct *pglist);

/**
 * @brief 在全局的存放内存区域结构体的数组中获得第num个内存区域结构体。
 * 
 * @param num 内存区域结构体的位置。
 * 
 * @return struct zone_struct *成功返回对应内存区域结构体的指针，失败返回NULL。
 */
extern struct zone_struct *get_global_zone(unsigned long num);

/**
 * @brief 在当指定pglist中获得第num个内存区域结构体。
 * 
 * @param pglist pglist 结构体。
 * @param num 内存区域结构体的位置。
 * 
 * @return struct zone_struct *成功返回对应内存区域结构体的指针，失败返回NULL。
 */
extern struct zone_struct *get_pglist_zone(
    struct pglist_struct *pglist, 
    unsigned long num
);

/**
 * @brief 在当前NUMA节点的pglist中获得第num个内存区域结构体。
 * 
 * @param num unsigned long 内存区域结构体的位置。
 * 
 * @return struct zone_struct *成功返回对应内存区域结构体的指针，失败返回NULL。
 */
#define get_current_pglist_zone(num)    \
    get_pglist_zone(get_current_pglist(), num)

/**
 * @brief 遍历系统中的所有 zone 结构体。
 * 
 * @param pos unsigned long 作为游标遍历所有 zone 结构体。
 * @param zone struct zone_struct* 存放当前遍历到的 zone 结构体。
 */
#define for_each_zone_gloabal(pos, zone)    \
    for (   (pos) = 0, (zone) = get_global_zone(0);   \
            (zone) != NULL;   \
            (pos)++, (zone) = get_global_zone(pos)    \
    )

/**
 * @brief 遍历指定 pglist 中的所有 zone 结构体。
 * 
 * @param pos unsigned long 作为游标遍历所有 zone 结构体。
 * @param pglist struct pglist_struct* pglist 结构体。
 * @param zone struct zone_struct* 存放当前遍历到的 zone 结构体。
 */
#define for_each_zone_pglist(pos, pglist, zone) \
    for (   (pos) = 0, (zone) = get_pglist_zone(pglist, 0); \
            (zone) != NULL; \
            (pos)++, (zone) = get_pglist_zone(pglist, pos)  \
    )

/**
 * @brief 遍历当前NUMA节点中的所有 zone 结构体。
 * 
 * @param pos unsigned long 作为游标遍历所有 zone 结构体。
 * @param zone struct zone_struct* 存放当前遍历到的 zone 结构体。
 */
#define for_each_zone_current(pos, zone)  \
    for_each_zone_pglist(pos, get_current_pglist(), zone)

/**
 * @brief 计算当前NUMA节点下可及的所有空闲页的数量。
 */
static inline unsigned long calculate_total_free_pages(void)
{
    unsigned long count = 0;
    unsigned long pos;
    struct zone_struct *zone;
    for_each_zone_current(pos, zone) {
        for (int i = 0; i < PAGE_MAX_ORDER; i++) {
            count += (1UL << i) * atomic_load(&(zone->free_area[i].free_count));
        }
    }
    return count;
}

#endif
